﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Syncfusion.Windows.PdfViewer;

namespace PdfSdk.PdfViewer
{
    /// <summary>
    /// internal class AsyncVList<T> : VirtualizationList<T>, INotifyCollectionChanged, INotifyPropertyChanged
    /// </summary>
    public class AsyncVListWrapper : ObjectWrapper
    {
        public AsyncVListWrapper(object obj) : base(obj)
        {
        }
    }
    /// <summary>
    /// internal class VirtualizationList<T> : IList<T>,
    /// </summary>
    public class VirtualizationListWrapper : ObjectWrapper
    {
        private readonly ScrollViewer m_virtualScroll;
        private readonly CustomVPanel m_virtualPanel;
        private readonly ImageProviderWrapper m_itemsProvider;
        public readonly DispatcherTimer m_dispatcherTimer;

        public VirtualizationListWrapper(object obj) : base(obj, obj.GetType().BaseType)
        {
            var itemsProvider = GetFieldValue("m_itemsProvider");
            if (itemsProvider == null)
                throw new ArgumentNullException("m_itemsProvider");
            m_itemsProvider = new ImageProviderWrapper(itemsProvider);

            m_virtualScroll = GetFieldValue("m_virtualScroll") as ScrollViewer;
            m_virtualPanel = GetFieldValue("m_virtualPanel") as CustomVPanel;
            m_dispatcherTimer = GetFieldValue("m_dispatcherTimer") as DispatcherTimer;
        }

        #region Custom

        private InkCanvas _fetchedCanvas;

        public event Action<int, InkCanvas> PageFetchedEx;

        private void OnPageFetched(int pageIndex)
        {
            PageFetchedEx?.Invoke(pageIndex, _fetchedCanvas);
        }

        public void AddCustomEventHandlers()
        {
            m_dispatcherTimer.Tick -= m_dispatcherTimer_Tick;
            m_dispatcherTimer.Tick += m_dispatcherTimer_Tick;
        }

        #endregion

        internal int NumberOfPagesInit => Convert.ToInt32(GetFieldValue());

        internal List<int> m_rend => GetFieldValue() as List<int>;

        internal List<int> m_zoomrend => GetFieldValue() as List<int>;

        internal bool m_iszoomchanged
        {
            get => Convert.ToBoolean(GetFieldValue());
            set => SetFieldValue(value);
        }

        // internal readonly Dictionary<int, IList<T>> m_pages = new Dictionary<int, IList<T>>()
        public Dictionary<int, IList<IEnumerable<Grid>>> m_pages => GetFieldValue() as Dictionary<int, IList<IEnumerable<Grid>>>;

        private double m_previousPosition
        {
            get => Convert.ToDouble(GetFieldValue());
            set => SetFieldValue(value);
        }

        internal DocumentView DocumentView => GetPropertyValue() as DocumentView;

        private void ClearPage(int pageIndex) => InvokeMethod("ClearPage", pageIndex);

        protected void RequestBackground(int pageIndex)
        {
            var page = Fetch(pageIndex);
            if (!m_itemsProvider.IsImageGenerated)
                return;

            Populate(pageIndex, page);
            LoadCompletedbackground(new object[2] { pageIndex, page });
        }

        protected void LoadCompletedbackground(object args)
        {
            InvokeMethod("LoadCompletedbackground", args);
        }

        internal bool CanRequest(int i) => Convert.ToBoolean(InvokeMethod("CanRequest", i));

        protected void Request(int pageIndex)
        {
            if (m_pages.ContainsKey(pageIndex))
                ClearPage(pageIndex);
            m_pages.Remove(pageIndex);
            m_pages.Add(pageIndex, null);

            object obj = Fetch(pageIndex);
            Populate(pageIndex, obj);
        }

        protected object Fetch(int pageIndex)
        {
            var obj = InvokeMethod("Fetch", pageIndex);
            _fetchedCanvas = m_itemsProvider._selectionCanvas;
            return obj;
        }

        protected void Populate(int pageIndex, object page) => InvokeMethod("Populate", pageIndex, page);

        private void ComputePageBounds() => InvokeMethod();

        private void m_dispatcherTimer_Tick(object sender, EventArgs e)
        {
            if (m_virtualScroll == null)
                return;
            if (m_virtualScroll.VerticalOffset == m_previousPosition && m_virtualPanel != null)
            {
                int firstVisibleItemIndex;
                int lastVisibleItemIndex;
                m_virtualPanel.VisibleRange(out firstVisibleItemIndex, out lastVisibleItemIndex);
                m_itemsProvider.CurrentPage = firstVisibleItemIndex;
                if (!Environment.Is64BitProcess)
                {
                    m_itemsProvider.LastPage = lastVisibleItemIndex;
                    if (m_itemsProvider.ImageSourceDictionary.Count > lastVisibleItemIndex - firstVisibleItemIndex + 1)
                    {
                        List<int> list = m_itemsProvider.ImageSourceDictionary.Keys.ToList();
                        for (int index = 0; index < list.Count; ++index)
                        {
                            if (list[index] < firstVisibleItemIndex || list[index] > lastVisibleItemIndex)
                            {
                                m_itemsProvider.ImageSourceDictionary[list[index]] = null;
                                m_itemsProvider.ImageSourceDictionary.Remove(list[index]);
                                GC.Collect();
                                GC.WaitForPendingFinalizers();
                            }
                        }
                        list.Clear();
                    }
                    if (m_pages.Count > lastVisibleItemIndex - firstVisibleItemIndex + 1)
                    {
                        List<int> list = m_pages.Keys.ToList();
                        for (int index = 0; index < list.Count; ++index)
                        {
                            if (list[index] < firstVisibleItemIndex || list[index] > lastVisibleItemIndex)
                            {
                                ClearPage(list[index]);
                                m_pages.Remove(list[index]);
                                if (m_rend.Contains(list[index]))
                                    m_rend.Remove(list[index]);
                            }
                        }
                        list.Clear();
                    }
                }
                if (firstVisibleItemIndex <= NumberOfPagesInit - 5 || firstVisibleItemIndex > NumberOfPagesInit - 5 && m_itemsProvider.IsinitializeCompleted)
                {
                    if (!Environment.Is64BitProcess)
                    {
                        for (int index = 0; index < m_itemsProvider.Count() - 1; ++index)
                        {
                            int num = index;
                            if (m_pages.ContainsKey(num) && (num < firstVisibleItemIndex || num > lastVisibleItemIndex))
                            {
                                if (m_pages.ContainsKey(num))
                                    ClearPage(num);
                                m_pages.Remove(num);
                                m_rend.Remove(num);
                            }
                        }
                    }
                    for (int index = firstVisibleItemIndex; index <= lastVisibleItemIndex; ++index)
                    {
                        _fetchedCanvas = null;

                        if (!m_itemsProvider.ImageSourceDictionary.ContainsKey(index))
                            m_itemsProvider.ImageGenerator(index);
                        if (m_itemsProvider.ImageSourceDictionary.ContainsKey(index) && !m_zoomrend.Contains(index))
                        {
                            RequestBackground(index);
                            m_zoomrend.Add(index);
                            if (index == lastVisibleItemIndex)
                                m_iszoomchanged = false;
                        }
                        else
                        {
                            #region Custom

                            if (CanRequest(index))
                            {
                                RequestBackground(index);
                                if (!m_zoomrend.Contains(index))
                                    m_zoomrend.Add(index);
                                if (index == lastVisibleItemIndex)
                                    m_iszoomchanged = false;
                            }

                            #endregion
                        }
                        if (CanRequest(index))
                        {
                            Request(index);
                            if (m_itemsProvider.IsImageGenerated)
                                m_rend.Add(index);
                            if (DocumentView != null)
                                DocumentView.GetWrapper().HideLoadingIndicator();
                            ComputePageBounds();

                            if (_fetchedCanvas != null)
                            {
                                OnPageFetched(index);
                                _fetchedCanvas = null;
                            }
                            break;
                        }
                        else
                        {
                            if (_fetchedCanvas != null)
                            {
                                OnPageFetched(index);
                                _fetchedCanvas = null;
                            }
                        }
                    }
                    if (Environment.Is64BitProcess)
                    {
                        for (int index = 0; index < m_itemsProvider.Count() - 1; ++index)
                        {
                            int key = index;
                            if (m_pages.ContainsKey(key) && (key < firstVisibleItemIndex - 1 || key > lastVisibleItemIndex + 1))
                            {
                                m_pages.Remove(key);
                                m_rend.Remove(key);
                            }
                        }
                    }
                }
                else if (DocumentView != null)
                    DocumentView.GetWrapper().DisplayLoadingIndicator();
            }
            m_previousPosition = m_virtualScroll.VerticalOffset;
        }
    }
}
